import { BigNumber, Contract, ethers } from "ethers";
import erc20Abi from '@/config/abi/erc20.json'
import presaleAbi from '@/config/abi/presale.json'
import { message } from "ant-design-vue";
import t from '@/utils/i18n'
import { isAddress } from "ethers/lib/utils";
import { BNB, USDT } from '@/config/constants/normalToken'
import { store } from "@/store";
import { JsonRpcSigner, WalletInfo, Web3Provider } from "@/types";
import { toRaw } from 'vue'

// @ts-ignore
const ethereum = window.ethereum;
export const getContract = (address: string, abi: object[], provider: Web3Provider | JsonRpcSigner) => {
    try {
        if (isAddress(address)) {
            try {
                return new ethers.Contract(address, abi, provider);
            } catch (err) {
                message.error(t('walletConnect.error'));
            }
        }
    } catch (err) {
        message.error(t("invalid.address"))
    }
};

export const getErc20Contract = (address: string) => {
    let signer:JsonRpcSigner;
    try {
        signer = toRaw(store.state.provider)?.getSigner();
    } catch (error) {
        signer = new ethers.providers.Web3Provider(ethereum)?.getSigner();
    }
    return getContract(address, erc20Abi, signer);
};

export const getPresaleContract = (address: string) => {
    let signer:JsonRpcSigner;
    try {
        signer = toRaw(store.state.provider)?.getSigner();
    } catch (error) {
        signer = new ethers.providers.Web3Provider(ethereum)?.getSigner();
    }
    return getContract(address, presaleAbi, signer);
};

export const isBNB = (tokenAddress: string) => {
    return tokenAddress.toLowerCase() === BNB.address.toLowerCase();
};

const getContractBalance = async (address: string | undefined, createContract: Function) => {
    const signer: JsonRpcSigner = toRaw(store.state.provider).getSigner();
    let balance: BigNumber = BigNumber.from(0);
    let name: string = 'BNB'
    let decimals = 18;
    if (typeof address === 'undefined') {
        balance = await signer.getBalance('pending');
        name = signer.provider._network.name.toUpperCase() || name
    }
    if (typeof address == 'string' && ethers.utils.isAddress(address)) {
        const contract: Contract | any = createContract(address) || Object
        if (typeof contract?.symbol == 'function') {
            name = await contract.symbol()
        } else {
            name = "Unknown"
        }
        if (typeof contract?.decimals == 'function') {
            decimals = await contract.decimals()
        } else if (typeof contract?._decimals == 'function') {
            decimals = await contract._decimals()
        }
        balance = await contract.balanceOf(store.state.account)
    }
    let walletInfo: WalletInfo = {
        ethName: name,
        balance: ethers.utils.formatUnits(balance, decimals)
    }
    return walletInfo;
}

export const getErc20Balance = (address: string | undefined) => {
    return getContractBalance(address, getErc20Contract);
};

export const getPresaleBalance = async (presaleAddress: string) => {
    const contract: Contract | any = getPresaleContract(presaleAddress) || Object
    const balance: BigNumber = await contract.balanceOf(store.state.account)
    return balance.toNumber();
};

export const getPresaleFundingBalance = async (presaleAddress: string) => {
    const contract: Contract | any = getPresaleContract(presaleAddress) || Object
    const balance: BigNumber = await contract.balanceOfFundingToken(store.state.account)
    return balance.toNumber();
};

export const getPresaleIsSuccess = async (presaleAddress: string) => {
    const contract: Contract | any = getPresaleContract(presaleAddress) || Object
    const _isSuccess: boolean = await contract._isSuccess()
    return _isSuccess;
};

export const getPresaleFundingGoal = async (presaleAddress: string) => {
    const contract: Contract | any = getPresaleContract(presaleAddress) || Object
    const _fundingGoal: BigNumber = await contract._fundingGoal()
    return _fundingGoal.toNumber();
};

export const getPresaleAmountRaised = async (presaleAddress: string) => {
    const contract: Contract | any = getPresaleContract(presaleAddress) || Object
    const _amountRaised: BigNumber = await contract._amountRaised()
    return _amountRaised.toNumber();
};

export const presaleWithdraw = async (presaleAddress: string) => {
    const contract: Contract | any = getPresaleContract(presaleAddress) || Object
    try {
        const withdraw = await contract.withdraw();
        return withdraw;
    } catch (error:any) {
        if(error.code === -32603){
            message.error(t("revert."+error.data.message.split(":")[1].replace(/\s/g,"")));
        }
    }
    return false;
};

export const presaleClaim = async (presaleAddress: string) => {
    const signer: JsonRpcSigner = toRaw(store.state.provider).getSigner()
    const contract: Contract | any = getPresaleContract(presaleAddress) || Object
    try {
        const claim = await contract.claim();
        return claim;
    } catch (error:any) {
        if(error.code === -32603){
            message.error(t("revert."+error.data.message.split(":")[1].replace(/\s/g,"")));
        }
    }
    return false;
};

export const presaleContribute = async (presaleAddress: string, inviteAddress: any , amount: number) => {
    if(!isAddress(inviteAddress)){
        inviteAddress = '0x0000000000000000000000000000000000000000';
    }
    const signer: JsonRpcSigner = toRaw(store.state.provider).getSigner()
    const contract: Contract | any = getPresaleContract(presaleAddress) || Object
    try {
        const contributeStatus = await contract.contribute(inviteAddress, amount);
        return contributeStatus;
    } catch (error:any) {
        if(error.code === -32603){
            message.error(t("revert."+error.data.message.split(":")[1].replace(/\s/g,"")));
        }
    }
    return false;
};

export const getErc20Approve = async (address: string, spender: string, amount: number) => {
    const signer: JsonRpcSigner = toRaw(store.state.provider).getSigner();
    const contract: Contract | any = getErc20Contract(address) || Object;
    try {
        const approveStatus = await contract.approve(spender, amount);
        return approveStatus;
    } catch (error:any) {
        if(error.code === 4001){
            message.error(t("presale.deniedTransactionSignature"));
        }
    }
    return false;
}

export const getErc20Allowance = async (address: string, owner: string, spender: string) => {
    const signer: JsonRpcSigner = toRaw(store.state.provider).getSigner();
    const contract: Contract | any = getErc20Contract(address) || Object;
    const allowance: BigNumber = await contract.allowance(owner, spender);
    return allowance.toNumber();
}